﻿namespace WebPacker.Packs.Watchers
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using WebPacker.Packs.Resources;

	public abstract class BaseResourceWatcher : IResourceWatcher, IDisposable
	{
		protected readonly IList<IResource> resources;
		protected readonly object syncRoot;
		private bool disposed;

		public BaseResourceWatcher()
		{
			resources = new List<IResource>();
			syncRoot = new object();
		}

		public abstract event EventHandler<ResourceChangedEventArgs> Changed;

		protected bool IsDisposed
		{
			get { return disposed; }
		}

		public abstract bool Accept(IResource resource);

		public virtual void Add(IResource resource)
		{
			Guard(resource);

			lock (syncRoot)
			{
				if (Contains(resource))
				{
					throw new ArgumentException("Resource is monitored allready.");
				}

				Watch(resource);
				resources.Add(resource);
			}
		}

		public virtual bool Remove(IResource resource)
		{
			Guard(resource);

			lock (syncRoot)
			{
				if (resources.Remove(resource))
				{
					Unwatch(resource);
					return true;
				}
			}

			return false;
		}

		public virtual bool Contains(IResource resource)
		{
			if (resource == null)
			{
				throw new ArgumentNullException("resource");
			}

			lock (syncRoot)
			{
				return resources.Contains(resource);
			}
		}

		public virtual void Set(IEnumerable<IResource> resources)
		{
			if (resources == null)
			{
				throw new ArgumentNullException("resources");
			}

			foreach (var resource in resources)
			{
				Guard(resource);
			}

			foreach (var resource in resources)
			{
				Add(resource);
			}
		}

		public virtual IEnumerable<IResource> GetResources()
		{
			lock (syncRoot)
			{
				return resources.ToArray();
			}
		}

		public virtual void Clear()
		{
			lock (syncRoot)
			{
				foreach (var resource in resources)
				{
					Unwatch(resource);
				}

				resources.Clear();
			}
		}

		public void Dispose()
		{
			Dispose(true);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (disposing && !disposed)
			{
				Clear();
				disposed = true;
			}
		}

		protected void Guard(IResource resource)
		{
			if (resource == null)
			{
				throw new ArgumentNullException("resource");
			}

			if (!Accept(resource))
			{
				throw new ArgumentException(string.Format("Unsupported type \"{0}\" for {1}.", resource.GetType(), GetType().Name));
			}
		}

		protected virtual void Watch(IResource resource)
		{
		}

		protected virtual void Unwatch(IResource resource)
		{
		}
	}
}
